/*
 * @Author: wangjun haodreams@163.com
 * @Date: 2022-07-21 20:24:12
 * @LastEditors: Wangjun
 * @LastEditTime: 2023-05-31 00:10:52
 * @FilePath: \ai_tradee:\go\src\gitee.com\haodreams\golib\winextend\window.go
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
package winextend

import (
	"fmt"
	"runtime"
	"syscall"
	"time"
	"unsafe"

	"github.com/lxn/win"
	"golang.org/x/sys/windows"
)

const (
	//默认的文本长度
	TextMaxLength = 1024
)

var libuser32 = windows.NewLazySystemDLL("user32.dll")
var findWindowEx = libuser32.NewProc("FindWindowExW")

var winextend *windows.LazyDLL
var windowFromPointXY *windows.LazyProc
var findWindowFromPidTitle *windows.LazyProc

func init() {
	if runtime.GOARCH == "amd64" {
		winextend = windows.NewLazyDLL("win64.dll")
	} else {
		winextend = windows.NewLazyDLL("win32.dll")
	}
	windowFromPointXY = winextend.NewProc("WindowFromPointXY")
	findWindowFromPidTitle = winextend.NewProc("FindWindowFromPidTitle")
}

func FindWindowFromPidTitle(pid uint32, title string) (hwnd win.HWND, err error) {
	err = findWindowFromPidTitle.Find()
	if err != nil {
		err = fmt.Errorf("function[%s]:%s", findWindowFromPidTitle.Name, err.Error())
		return
	}
	ptr, err := syscall.UTF16PtrFromString(title)
	if err != nil {
		return
	}
	ret, _, _ := syscall.SyscallN(findWindowFromPidTitle.Addr(), uintptr(pid), uintptr(unsafe.Pointer(ptr)))
	hwnd = win.HWND(ret)
	return
}

// 原生的有问题,y参数不能传递
func WindowFromPoint(x, y int) (hwnd win.HWND, err error) {
	err = windowFromPointXY.Find()
	if err != nil {
		err = fmt.Errorf("function[%s]:%s", windowFromPointXY.Name, err.Error())
		return
	}
	ret, _, _ := syscall.SyscallN(windowFromPointXY.Addr(), uintptr(x), uintptr(y))
	hwnd = win.HWND(ret)
	return
}

/**
 * @description: 查找指定的窗体
 * @param {win.HWND} hwndParent
 * @param {win.HWND} hwndChildAfter
 * @param {*} lpClassName
 * @param {*uint16} lpWindowName
 * @return {*}
 */
func FindWindowEx(hwndParent win.HWND, hwndChildAfter win.HWND, lpClassName, lpWindowName *uint16) win.HWND {
	ret, _, _ := syscall.SyscallN(findWindowEx.Addr(),
		uintptr(hwndParent),
		uintptr(hwndChildAfter),
		uintptr(unsafe.Pointer(lpClassName)),
		uintptr(unsafe.Pointer(lpWindowName)),
		0, 0)
	return win.HWND(ret)
}

/**
 * @description: FindWindowEx 的简化版,FindWindow 用于查找主窗体，不能查找窗体内的对话框按钮等资源，如果需要窗体内资源用EnumWindows
 * @param {win.HWND} hwndParent
 * @param {win.HWND} hwndChildAfter
 * @return {*}
 */
func FindWindow(hwndParent win.HWND, hwndChildAfter win.HWND) win.HWND {
	return FindWindowEx(hwndParent, hwndChildAfter, nil, nil)
}

/**
 * @description: 获取窗体的标题 不用GetWindowText()是在某些情况下不能获取到窗体
 * @param {win.HWND} hwnd
 * @return {*}
 */
func GetWindowText(hwnd win.HWND) string {
	text := make([]uint16, TextMaxLength)
	size := win.SendMessage(hwnd, win.WM_GETTEXT, 255, uintptr(unsafe.Pointer(&text[0])))
	if size > 0 {
		if size > TextMaxLength {
			size = TextMaxLength
		}
		return windows.UTF16ToString(text[:size])
	}
	return ""
}

/**
 * @description: 获取窗体的标题 不用GetWindowText()是在某些情况下不能获取到窗体
 * @param {win.HWND} hwnd
 * @return {*}
 */
func SetWindowText(hwnd win.HWND, text string) {
	ptr, err := syscall.UTF16PtrFromString(text)
	if err != nil {
		return
	}
	size := win.SendMessage(hwnd, win.WM_SETTEXT, 0, uintptr(unsafe.Pointer(ptr)))
	if size > 0 {
		if size > TextMaxLength {
			size = TextMaxLength
		}
		return
	}
	return
}

/**
 * @description: 获取进程的主窗体
 * @param {uint32} pid
 * @return {*}
 */
func GetWindowFromPID(pid uint32) (hwnd win.HWND) {
	id := uint32(0)
	for {
		hwnd = FindWindow(0, hwnd)
		if hwnd == 0 {
			return
		}

		win.GetWindowThreadProcessId(hwnd, &id)
		if id == pid {
			//text := GetWindowText(hwnd)
			//log.Println(text)

			if (win.GetWindowLong(hwnd, win.GWL_STYLE) & win.WS_VISIBLE) != 0 {
				return GetTopWinowd(hwnd)
			}
			//log.Println(id, GetWindowText(hwnd))
			//return GetTopWinowd(hwnd)
		}
	}
}

/**
 * @description: 获取顶层的可见窗口
 * @param {win.HWND} hwnd
 * @return {*}
 */
func GetTopWinowd(hwnd win.HWND) win.HWND {
	h := hwnd
	for {
		h = win.GetParent(h)
		if h == 0 {
			return hwnd
		}
		//text := GetWindowText(h)
		//log.Println(text)
		if (win.GetWindowLong(h, win.GWL_STYLE) & win.WS_VISIBLE) != 0 {
			return h
		}
	}
}

func Click() {
	mi := new(win.MOUSE_INPUT)
	mi.Type = win.INPUT_MOUSE
	mi.Mi.DwFlags = win.MOUSEEVENTF_LEFTDOWN
	win.SendInput(1, unsafe.Pointer(mi), int32(unsafe.Sizeof(*mi)))

	time.Sleep(time.Millisecond * 5)

	mi.Mi.DwFlags = win.MOUSEEVENTF_LEFTUP
	win.SendInput(1, unsafe.Pointer(mi), int32(unsafe.Sizeof(*mi)))
}

func RightClick() {
	mi := new(win.MOUSE_INPUT)
	mi.Type = win.INPUT_MOUSE
	mi.Mi.DwFlags = win.MOUSEEVENTF_RIGHTDOWN
	win.SendInput(1, unsafe.Pointer(mi), int32(unsafe.Sizeof(*mi)))

	time.Sleep(time.Millisecond * 5)

	mi.Mi.DwFlags = win.MOUSEEVENTF_RIGHTUP
	win.SendInput(1, unsafe.Pointer(mi), int32(unsafe.Sizeof(*mi)))
}

// 自动键入数字
func TypeStr(str string) {
	for i := 0; i < len([]rune(str)); i++ {
		ustr := uint16(CharCodeAt(str, i))
		UnicodeType(ustr)
		time.Sleep(time.Millisecond * 5)
	}
}

// CharCodeAt char code at utf-8
func CharCodeAt(s string, n int) rune {
	i := 0
	for _, r := range s {
		if i == n {
			return r
		}
		i++
	}
	return 0
}

// UnicodeType tap uint32 unicode
func UnicodeType(value uint16) {

	input := make([]win.KEYBD_INPUT, 2)

	input[0].Type = win.INPUT_KEYBOARD
	input[0].Ki.WVk = 0
	input[0].Ki.WScan = (value)
	input[0].Ki.DwFlags = win.KEYEVENTF_UNICODE // KEYEVENTF_UNICODE;

	input[1].Type = win.INPUT_KEYBOARD
	input[1].Ki.WVk = 0
	input[1].Ki.WScan = (value)
	input[1].Ki.DwFlags = win.KEYEVENTF_KEYUP | win.KEYEVENTF_UNICODE // KEYEVENTF_UNICODE;

	win.SendInput(2, unsafe.Pointer(&input[0]), int32(unsafe.Sizeof(input[0])))
}

func PressKey(vk uint16) {
	input := make([]win.KEYBD_INPUT, 1)

	input[0].Type = win.INPUT_KEYBOARD
	input[0].Ki.WVk = vk
	win.SendInput(1, unsafe.Pointer(&input[0]), int32(unsafe.Sizeof(input[0])))
}

func PressTowKey(vk1, vk2 uint16) {
	input := make([]win.KEYBD_INPUT, 4)

	input[0].Type = win.INPUT_KEYBOARD
	input[1].Type = win.INPUT_KEYBOARD
	input[2].Type = win.INPUT_KEYBOARD
	input[3].Type = win.INPUT_KEYBOARD
	input[0].Ki.WVk = vk1
	input[1].Ki.WVk = vk2

	input[2].Ki.WVk = vk1
	input[3].Ki.WVk = vk2

	input[2].Ki.DwFlags = win.KEYEVENTF_KEYUP
	input[3].Ki.DwFlags = win.KEYEVENTF_KEYUP
	win.SendInput(4, unsafe.Pointer(&input[0]), int32(unsafe.Sizeof(input[0])))
}
